﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Diagnostics;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.WebControls;
using System.Xml;

namespace Riolinx.SocialConnekt.Community
{
    public class ClaimProvider : SPClaimProvider
    {

        // Labels for our nodes in the People Picker.
        private string[] nodeLabels = new string[] { "Email Address", "Social Network", "Social Id" };

        // Keys for our People Picker hierarchy. 
        private string[] nodeKeys = new string[] { "email", "authprovider", "socialid" };

        // Social Networks that we are using.
        private string[] supportedSocialNetworks = new string[] { "Windows Live", "Yahoo", "LinkedIn", "Facebook" };



        public ClaimProvider(string displayName)
            : base(displayName)
        {
        }

        protected override void FillClaimTypes(List<string> claimTypes)
        {
            if (claimTypes == null)
                throw new ArgumentNullException("claimTypes");

            // Add our claim type.

            claimTypes.Add(EmailClaimType);
            claimTypes.Add(AuthProviderClaimType);
            claimTypes.Add(SocialIdClaimType);

        }

        protected override void FillClaimValueTypes(List<string> claimValueTypes)
        {
            if (claimValueTypes == null)
                throw new ArgumentNullException("claimValueTypes");

            // Add our claim value type.

            claimValueTypes.Add(EmailClaimValueType);
            claimValueTypes.Add(AuthProviderClaimValueType);
            claimValueTypes.Add(SocialIdClaimValueType);

        }

        protected override void FillClaimsForEntity(Uri context, SPClaim entity, List<SPClaim> claims)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            if (claims == null)
                throw new ArgumentNullException("claims");

            //figure out who the user is so we know if we need to augment his claims
            string strUserId = entity.Value;

            SPSecurityTokenServiceManager manager = SPSecurityTokenServiceManager.Local;

            SPTrustedLoginProvider scProvider = manager.TrustedLoginProviders[Constants.SOCIALCONNEKT_PROVIDER_NAME];


            if (scProvider != null)
            {

                //get the request envelope with the claims information
                string rqst =
         System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString();

                //create a list to store the results
                List<IncomingClaim> incomingClaims = new List<IncomingClaim>();

                //create an Xml document for parsing the results and load the data
                XmlDocument xDoc = new XmlDocument();
                xDoc.LoadXml(rqst);

                //create a new namespace table so we can query
                XmlNamespaceManager xNS = new XmlNamespaceManager(xDoc.NameTable);

                //add the namespaces we'll be using
                xNS.AddNamespace("s", "http://www.w3.org/2003/05/soap-envelope");
                xNS.AddNamespace("trust", "http://docs.oasis-open.org/ws-sx/ws-trust/200512");
                xNS.AddNamespace("saml", "urn:oasis:names:tc:SAML:1.0:assertion");

                //get the list of claim nodes
                XmlNodeList xList =
         xDoc.SelectNodes("s:Envelope/s:Body/trust:RequestSecurityToken/trust:OnBehalfOf/saml:Assertion/saml:AttributeStatement/saml:Attribute", xNS);

                //get the matches
                if ((xList != null) && (xList.Count > 0))
                {
                    //enumerate through the matches to get the claim list
                    foreach (XmlNode xNode in xList)
                    {
                        //get the claim type first
                        string currentClaimType =
                              xNode.Attributes["AttributeNamespace"].Value +
                              "/" + xNode.Attributes["AttributeName"].Value;

                        //if (currentClaimType != SocialIdClaimType)
                        //{

                        //each claim type will have one to many child nodes with the values
                        foreach (XmlNode claimNode in xNode.ChildNodes)
                        {
                            incomingClaims.Add(new IncomingClaim(currentClaimType,
                                   claimNode.InnerText));

                        }

                    }

                }


                string strIdentityClaimType = scProvider.IdentityClaimTypeInformation.MappedClaimType;



                foreach (IncomingClaim incomingClaim in incomingClaims)
                {
                    bool bAddToClaims = !scProvider.ClaimTypes.Contains<string>(incomingClaim.claimType);
                    //bool b = ;
                    //if (bAddToClaims)
                    string s;
                    //bool b = s.Contains(incomingClaim.claimType, StringComparer.CurrentCultureIgnoreCase);
                    if(!scProvider.ClaimTypes.Any(x => x.Contains(incomingClaim.claimType)))
                    {
                        claims.Add(CreateClaimForSTS(incomingClaim.claimType, Microsoft.IdentityModel.Claims.ClaimValueTypes.String, incomingClaim.claimValue));
                    }
                }

                //if (strUserId.Contains(AssociatedTrustedLoginProviderName.ToLower()))
                //{
                //    SPClaim c = null;
                //    try
                //    {
                //        c = CreateClaimForSTS(GenderClaimType, GenderClaimValueType, "Male");
                //    }
                //    catch (Exception ex)
                //    {

                //        string s = ex.ToString();
                //    }

                //    claims.Add(c);
                //}
            }
        }



        private SPClaim CreateClaimForSTS(string claimType, string claimValueType, string claimValue)
        {

            SPClaim result = new SPClaim(claimType,
                                        claimValue,
                  claimValueType,
                  SPOriginalIssuers.Format
                                  (SPOriginalIssuerType.TrustedProvider,
                                   AssociatedTrustedLoginProviderName));

            return result;
        }


        protected override void FillEntityTypes(List<string> entityTypes)
        {
            // Return the type of entity claim that we are using (only one in this case).
            entityTypes.Add(SPClaimEntityTypes.Trusted);
            entityTypes.Add(EmailClaimType);
            entityTypes.Add(AuthProviderClaimType);
            entityTypes.Add(SocialIdClaimType);

        }

        protected override void FillHierarchy(Uri context, string[] entityTypes, string hierarchyNodeID, int numberOfLevels, SPProviderHierarchyTree hierarchy)
        {
            // Ensure that People Picker is asking for the type of entity that we 
            // return; site collection administrator will not return, for example.
            if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.Trusted))
                return;

            // Check to see whether the hierarchyNodeID is null; it is when the control 
            // is first loaded, but if a user clicks on one of the nodes, it will return
            // the key of the node that was clicked. This lets you build out a 
            // hierarchy as a user clicks something, instead of all at once. So I 
            // wrote the code for that scenario, but I am not using it in that way.  
            // Which means that I could have just as easily used an 
            // if (hierarchyNodeID == null) in this particular implementation, instead
            // of a switch statement.
            switch (hierarchyNodeID)
            {
                case null:
                    // When it first loads, add all our nodes.

                    //Adds the Email Address node
                    hierarchy.AddChild(new
                          Microsoft.SharePoint.WebControls.SPProviderHierarchyNode(
                          ProviderInternalName,
                          nodeLabels[0],
                          nodeKeys[0],
                          true));

                    //Adds the Social Network node
                    hierarchy.AddChild(new
                          Microsoft.SharePoint.WebControls.SPProviderHierarchyNode(
                          ProviderInternalName,
                          nodeLabels[1],
                          nodeKeys[1],
                          true));

                    //Adds the Social Id node
                    hierarchy.AddChild(new
                          Microsoft.SharePoint.WebControls.SPProviderHierarchyNode(
                          ProviderInternalName,
                          nodeLabels[2],
                          nodeKeys[2],
                          true));


                    break;
                default:
                    break;
            }

        }

        protected override void FillResolve(Uri context, string[] entityTypes, SPClaim resolveInput, List<PickerEntity> resolved)
        {
            // Ensure that People Picker is asking for the type of entity that we 
            // return; site collection administrator will not return, for example.
            if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.Trusted))
                return;

            string claimTypeName = string.Empty;
            string claimValue = string.Empty;

            // Handles only email or auth provider claim types
            if (null != resolveInput && (resolveInput.ClaimType == EmailClaimType || resolveInput.ClaimType == AuthProviderClaimType || resolveInput.ClaimType == SocialIdClaimType))
            {
                claimValue = resolveInput.Value;
                claimTypeName = GetName(resolveInput.ClaimType);

                PickerEntity entity = CreatePickerEntity();
                //entity.Claim = resolveInput;
                entity.Claim = CreateClaimForSTS(resolveInput.ClaimType, resolveInput.ValueType, resolveInput.Value);
                entity.Description = resolveInput.ClaimType + " = " + claimValue;
                if (resolveInput.ClaimType != SocialIdClaimType)
                {
                    entity.DisplayText = claimTypeName + " = " + claimValue;
                    entity.EntityData[PeopleEditorEntityDataKeys.DisplayName] = claimTypeName + " = " + claimValue;
                }
                else
                {
                    entity.DisplayText = claimValue;
                    entity.EntityData[PeopleEditorEntityDataKeys.DisplayName] = claimValue;
                }

                entity.EntityType = resolveInput.ClaimType;
                entity.IsResolved = true;
                resolved.Add(entity);
            }


            //// Same sort of code as in search, to validate that we have a match.
            //foreach (string team in ourTeams)
            //{
            //    if (team.ToLower() == resolveInput.Value.ToLower())
            //    {
            //        // We have a match; create a matching entity with helper method.
            //        PickerEntity pe = GetPickerEntity(team);

            //        // Add it to the return list of picker entries.
            //        resolved.Add(pe);
            //    }
            //}

        }

        /// <summary>
        /// Gets the name from a fully qualified name.
        /// </summary>
        /// <param name="fullName">The full name. </param>
        /// <returns>a name string. </returns>
        private string GetName(string fullName)
        {
            string[] aName;
            char[] aDelims = new Char[] { '/', '\\' };

            aName = fullName.Split(aDelims);
            return aName[aName.GetUpperBound(0)];
        }

        protected override void FillResolve(Uri context, string[] entityTypes, string resolveInput, List<PickerEntity> resolved)
        {
            // Ensure that People Picker is asking for the type of entity that we 
            // return; site collection administrator will not return, for example.
            if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.Trusted))
                return;

            string claimTypeName = string.Empty;
            string claimValue = string.Empty;

            // Handles only email or auth provider claim types
            if (null != resolveInput)
            {
                claimValue = resolveInput.ToLower();
                //claimTypeName = GetName(resolveInput.ClaimType);

                PickerEntity entity = CreatePickerEntity();
                entity.Claim = CreateClaimForSTS(SocialIdClaimType, SocialIdClaimValueType, claimValue);
                entity.Description = claimValue;
                entity.DisplayText = claimValue;
                //entity.EntityData[PeopleEditorEntityDataKeys.] = claimTypeName + " = " + claimValue;
                entity.EntityType = SocialIdClaimType;
                entity.IsResolved = true;

                //entity.Claim = resolveInput;
                //entity.Description = resolveInput.ClaimType + " = " + claimValue;
                //entity.DisplayText = claimTypeName + " = " + claimValue;
                //entity.EntityData[PeopleEditorEntityDataKeys.DisplayName] = claimTypeName + " = " + claimValue;
                //entity.EntityType = SPClaimEntityTypes.User;
                //entity.IsResolved = true;
                resolved.Add(entity);
            }
        }

        protected override void FillSchema(SPProviderSchema schema)
        {
            // Add the schema element that we need at a minimum in our picker node.
            schema.AddSchemaElement(new
                  SPSchemaElement(PeopleEditorEntityDataKeys.DisplayName,
                  "Display Name", SPSchemaElementType.Both));
        }

        protected override void FillSearch(Uri context, string[] entityTypes, string searchPattern, string hierarchyNodeID, int maxCount, SPProviderHierarchyTree searchTree)
        {

            // Ensure that People Picker is asking for the type of entity that we 
            // return; site collection administrator will not return, for example.
            if (!EntityTypesContain(entityTypes, SPClaimEntityTypes.User))
                return;

            // The node where we will place our matches.
            Microsoft.SharePoint.WebControls.SPProviderHierarchyNode matchNode = null;

            if (IsValidEmail(searchPattern))
            {
                PickerEntity pe = GetPickerEntity(EmailClaimType, searchPattern.ToLower(), Microsoft.IdentityModel.Claims.ClaimValueTypes.String, "Email Address");

                // If we did not have a hierarchy, we would add it here
                // by using the list described previously.
                // matches.Add(pe);

                // Add the team node where it should be displayed; 
                // ensure that we have not already added a node to the tree
                // for this team's location.
                if (!searchTree.HasChild(nodeLabels[1]))
                {
                    // Create the node so that we can show our match in there too.
                    matchNode = new
                    SPProviderHierarchyNode(ProviderInternalName,
                    nodeLabels[0],
                    nodeKeys[0],
                    true);

                    // Add it to the tree.
                    searchTree.AddChild(matchNode);
                }
                else
                    // Get the node for this team.
                    matchNode = searchTree.Children.Where(theNode =>
                    theNode.HierarchyNodeID == nodeKeys[1]).First();

                // Add the picker entity to our tree node.
                matchNode.AddEntity(pe);

            }
            else
            {
                // We need to create a match for any entered value as it can be the Social Id of the user
                PickerEntity peSocialId = GetPickerEntity(SocialIdClaimType, searchPattern.ToLower(), Microsoft.IdentityModel.Claims.ClaimValueTypes.String, "Social Id");
                matchNode = new
                           SPProviderHierarchyNode(ProviderInternalName,
                           nodeLabels[2],
                           nodeKeys[2],
                           true);
                searchTree.AddChild(matchNode);
                matchNode.AddEntity(peSocialId);

                // Look to see whether the value that is typed in also matches any of our social networks.
                foreach (string socialNetworkName in supportedSocialNetworks)
                {
                    // Increment team node tracker.
                    //nodeCounter += 1;

                    // Simple way to do a string comparison to the search criteria.
                    // This way, all a person has to type in to find Yahoo! is "y".
                    if (socialNetworkName.ToLower().StartsWith(searchPattern.ToLower()))
                    {
                        // We have a match, create a matching entity.
                        // This is a helper method that I will explain later.
                        PickerEntity peSocialNetwork = GetPickerEntity(AuthProviderClaimType, socialNetworkName, Microsoft.IdentityModel.Claims.ClaimValueTypes.String, "Social Network");

                        // If we did not have a hierarchy, we would add it here
                        // by using the list described previously.
                        // matches.Add(pe);

                        // Add the team node where it should be displayed; 
                        // ensure that we have not already added a node to the tree
                        // for this team's location.
                        if (!searchTree.HasChild(nodeLabels[1]))
                        {
                            // Create the node so that we can show our match in there too.
                            matchNode = new
                            SPProviderHierarchyNode(ProviderInternalName,
                            nodeLabels[1],
                            nodeKeys[1],
                            true);

                            // Add it to the tree.
                            searchTree.AddChild(matchNode);
                        }
                        else
                            // Get the node for this team.
                            matchNode = searchTree.Children.Where(theNode => theNode.HierarchyNodeID == nodeKeys[1]).First();

                        // Add the picker entity to our tree node.
                        matchNode.AddEntity(peSocialNetwork);
                    }
                }
            }




        }

        private PickerEntity GetPickerEntity(string ClaimType, string ClaimValue, string ClaimValueType, string EntityGroupName)
        {

            // Use the helper function!
            PickerEntity pe = CreatePickerEntity();

            // Set the claim that is associated with this match.
            pe.Claim = CreateClaim(ClaimType, ClaimValue, ClaimValueType);

            // Set the tooltip that is displayed when you pause over the resolved claim.
            pe.Description = ClaimProvider.ProviderDisplayName + ":" + ClaimValue;

            // Set the text that we will display.
            pe.DisplayText = ClaimValue;

            // Store it here, in the hashtable **
            pe.EntityData[PeopleEditorEntityDataKeys.DisplayName] = ClaimValue;

            // We plug this in as a role type entity.
            pe.EntityType = SPClaimEntityTypes.Trusted;

            // Flag the entry as being resolved.
            pe.IsResolved = true;

            // This is the first part of the description that shows
            // above the matches, like Role: Forms Auth when
            // you do an forms-based authentication search and find a matching role.
            pe.EntityGroupName = EntityGroupName;

            return pe;
        }

        bool IsValidEmail(string strIn)
        {
            // Return true if strIn is in valid e-mail format.
            return System.Text.RegularExpressions.Regex.IsMatch(strIn, @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
        }

        internal static string ProviderDisplayName
        {
            get
            {
                return "SocialConnekt (Advanced)";
            }
        }


        internal static string ProviderInternalName
        {
            get
            {
                return "SocialConnektClaimProvider";
            }
        }


        // The AssociatedTrustedLoginProviderName property is used for 
        // generating the SPClaim object. 
        public string AssociatedTrustedLoginProviderName
        {
            get { return "SocialConnekt"; }
        }


        public override string Name
        {
            get
            {
                return ProviderInternalName;
            }
        }

        public override bool SupportsEntityInformation
        {
            get
            {
                return true;
            }
        }

        public override bool SupportsHierarchy
        {
            get
            {
                return true;
            }
        }

        public override bool SupportsResolve
        {
            get
            {
                return true;
            }
        }

        public override bool SupportsSearch
        {
            get
            {
                return true;
            }
        }

        #region "Claim Types"

        private static string GenderClaimType
        {
            get
            {
                return "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/gender";
            }
        }

        private static string EmailClaimType
        {
            get
            {
                return "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";
            }
        }

        private static string SocialIdClaimType
        {
            get
            {
                return "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/socialid";
            }
        }

        private static string AuthProviderClaimType
        {
            get
            {
                return "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/authenticationprovider";
            }
        }

        private static string EmailClaimValueType
        {
            get
            {
                return Microsoft.IdentityModel.Claims.ClaimValueTypes.String;
            }
        }

        private static string SocialIdClaimValueType
        {
            get
            {
                return Microsoft.IdentityModel.Claims.ClaimValueTypes.String;
            }
        }

        private static string AuthProviderClaimValueType
        {
            get
            {
                return Microsoft.IdentityModel.Claims.ClaimValueTypes.String;
            }
        }
        #endregion

        private class IncomingClaim
        {
            public string claimType { get; set; }
            public string claimValue { get; set; }

            public IncomingClaim(string claimType, string claimValue)
            {
                this.claimType = claimType;
                this.claimValue = claimValue;
            }
        }
    }


}
